﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GSdk.Net.Lglcd.InteropServices
{
    using Methodsx86 = x86.Methods;
    using Methodsx64 = x64.Methods;

    /// <summary>
    /// Methods contained in this class automatically calls 64bit or 32bit methods, to allow "Any CPU" compiles
    /// </summary>
    static class InteropMethodsWrapper
    {
        public static bool Initialized { get; private set; }

        public static void Initialize()
        {
            if (!Initialized)
                if (Environment.Is64BitProcess)
                    Methodsx64.lgLcdInit();
                else
                    Methodsx86.lgLcdInit();
            Initialized = true;
        }

        public static void DeInitialize()
        {
            if (Initialized)
                if (Environment.Is64BitProcess)
                    Methodsx64.lgLcdDeInit();
                else
                    Methodsx86.lgLcdDeInit();
            Initialized = false;
        }

        public static void Connect(Applet applet)
        {
            uint result = (uint)Errors.ERROR_SUCCESS;
            if (Environment.Is64BitProcess)
                result = Methodsx64.lgLcdConnectExW(ref applet.m_ConnectionContext);
            else
                result = Methodsx86.lgLcdConnectExW(ref applet.m_ConnectionContext);

            if (result != (uint)Errors.ERROR_SUCCESS)
                switch (result)
                {
                    default:
                        throw new AppletException(errorCode: result);
                }

            if (applet.m_ConnectionContext.connection == (int)DescriptorErrors.LGLCD_INVALID_CONNECTION)
                throw new AppletException("Invalid descriptor");
        }

        public static void Disconnect(Applet applet)
        {
            uint result = (uint)Errors.ERROR_SUCCESS;
            if (Environment.Is64BitProcess)
                result = Methodsx64.lgLcdDisconnect(applet.m_ConnectionContext.connection);
            else
                result = Methodsx86.lgLcdDisconnect(applet.m_ConnectionContext.connection);

            if (result != (uint)Errors.ERROR_SUCCESS)
                switch (result)
                {
                    default:
                        throw new AppletException(errorCode: result);
                }

            applet.m_ConnectionContext.connection = (int)DescriptorErrors.LGLCD_INVALID_CONNECTION;
        }

        public static bool Update(Device device, IntPtr bitmapStruct)
        {
            uint result = (uint)Errors.ERROR_SUCCESS;

            if (Environment.Is64BitProcess)
                result = Methodsx64.lgLcdUpdateBitmap(device.m_OpenByTypeContext.device, bitmapStruct, (Priorities)(((uint)device.UpdateStyle) | ((uint)device.AppletPriority)));
            else
                result = Methodsx86.lgLcdUpdateBitmap(device.m_OpenByTypeContext.device, bitmapStruct, (Priorities)(((uint)device.UpdateStyle) | ((uint)device.AppletPriority)));

            // This is used to avoid peformance overhead because the situation where a device is disconnected is quite common
            if (result == (uint)Errors.ERROR_DEVICE_NOT_CONNECTED)
                return false;

            if (result != (uint)Errors.ERROR_SUCCESS)
                switch (result)
                {
                    default:
                        throw new DeviceException(errorCode: result);
                }

            return true;
        }

        public static void Open(Device device)
        {
            uint result = (uint)Errors.ERROR_SUCCESS;

            if (Environment.Is64BitProcess)
                result = Methodsx64.lgLcdOpenByType(ref device.m_OpenByTypeContext);
            else
                result = Methodsx86.lgLcdOpenByType(ref device.m_OpenByTypeContext);

            if (result != (uint)Errors.ERROR_SUCCESS)
                switch (result)
                {
                    default:
                        throw new DeviceException(errorCode: result);
                }

            if (device.m_OpenByTypeContext.device == (int)DescriptorErrors.LGLCD_INVALID_DEVICE)
                throw new DeviceException("Invalid device descriptor");
        }

        public static void Close(Device device)
        {
            uint result = (uint)Errors.ERROR_SUCCESS;
            if (Environment.Is64BitProcess)
                result = Methodsx64.lgLcdClose(device.m_OpenByTypeContext.device);
            else
                result = Methodsx86.lgLcdClose(device.m_OpenByTypeContext.device);

            if (result != (uint)Errors.ERROR_SUCCESS)
                switch (result)
                {
                    default:
                        throw new DeviceException(errorCode: result);
                }

            device.m_OpenByTypeContext.device = (int)DescriptorErrors.LGLCD_INVALID_DEVICE;
        }

        public static uint ReadSoftButtons(Device device)
        {
            Buttons tmp = Buttons.None;
            if (Environment.Is64BitProcess)
                Methodsx64.lgLcdReadSoftButtons(device.m_OpenByTypeContext.device, ref tmp);
            else
                Methodsx86.lgLcdReadSoftButtons(device.m_OpenByTypeContext.device, ref tmp);

            return (uint)tmp;
        }

        public static void SetAsForegroundApplet(Device device, bool foregroundApplet)
        {
            uint result = (uint)Errors.ERROR_SUCCESS;

            if (Environment.Is64BitProcess)
                result = Methodsx64.lgLcdSetAsLCDForegroundApp(device.m_OpenByTypeContext.device, (foregroundApplet ? ForegroundYesNoFlags.LGLCD_LCD_FOREGROUND_APP_YES : ForegroundYesNoFlags.LGLCD_LCD_FOREGROUND_APP_NO));
            else
                result = Methodsx86.lgLcdSetAsLCDForegroundApp(device.m_OpenByTypeContext.device, (foregroundApplet ? ForegroundYesNoFlags.LGLCD_LCD_FOREGROUND_APP_YES : ForegroundYesNoFlags.LGLCD_LCD_FOREGROUND_APP_NO));

            if (result != (uint)Errors.ERROR_SUCCESS)
                switch (result)
                {
                    default:
                        throw new DeviceException(errorCode: result);
                }

            if (device.m_OpenByTypeContext.device == (int)DescriptorErrors.LGLCD_INVALID_DEVICE)
                throw new DeviceException("Invalid device descriptor");
        }
    }
}
